import requests  # 导入requests库，用于发送HTTP请求
import json  # 导入json库，用于处理JSON数据
import time

def get_kw_headers():
    """
    获取酷我音乐搜索所需的请求头信息。
    返回:
        dict: 包含User-Agent、Referer和Content-Type的字典
    """
    return {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
        "Referer": "https://www.kuwo.cn/",
        "Content-Type": "application/json"
    }

def search_music(keyword, page=1, rn=30):
    """
    根据关键词搜索酷我音乐，并返回指定页码和数量的结果。
    参数:
        keyword (str): 搜索的关键词，如歌手名、歌曲名等
        page (int): 页码，默认为第1页
        rn (int): 每页返回的结果数量，默认为30条

    返回:
        list: 包含搜索结果的列表，如果请求失败则返回None
    """
    # 酷我音乐搜索音乐的API URL
    url = 'https://www.kuwo.cn/search/searchMusicBykeyWord?vipver=1&client=kt&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&mobi=1&issubtitle=1&show_copyright_off=1'
    # 请求参数，包括搜索关键词、页码和每页结果数量
    params = {
        "all": keyword,  # 搜索关键词，使用'all'字段表示全字段搜索
        "pn": page,  # 页码
        "rn": rn  # 每页结果数量
    }
    # 发送GET请求，附带请求头和参数
    resp = requests.get(url, headers=get_kw_headers(), params=params)
    # 检查响应状态码是否为200（表示请求成功）
    if resp.status_code == 200:
        try:
            # 解析响应的JSON数据
            data = resp.json()
            # 从解析后的数据中提取'abslist'字段，该字段包含搜索结果的列表
            abslist = data.get("abslist", [])
            MUSICRID = [item['MUSICRID'] for item in abslist]
            # 处理每个字符串，截取掉前面的6个字符，保存后面的全部内容
            MUSICRID = [item[6:] for item in MUSICRID]
            return MUSICRID
        except json.JSONDecodeError:
            # 如果JSON解析失败，打印错误信息并返回None
            print("响应数据不是有效的JSON格式")
            return None
    else:
        # 如果请求不成功，打印状态码和错误信息
        print(f"请求失败，状态码: {resp.status_code}")
        return None

def save_to_json(data, filename='news.json'):
    """
    将数据保存为JSON文件。
    参数:
        data (dict or list): 要保存的数据
        filename (str): 保存的文件名，默认为'news.json'
    """
    try:
        with open(filename, 'w', encoding='utf-8') as f:
            # 使用json.dump将数据写入文件，ensure_ascii=False确保中文字符正常显示，indent=4使JSON格式化更美观
            json.dump(data, f, ensure_ascii=False, indent=4)
        print(f"数据已成功保存到 {filename}")
    except IOError as e:
        # 捕获文件操作中的IO错误并打印错误信息
        print(f"保存文件失败: {e}")

# 创建存储图片的文件夹
import os
def mkdir_dir_at_curr_path(dir_name):
    """
    在当前目录下创建指定名称的文件夹
    :param dir_name: 要创建的文件夹名称
    """
    try:
        # 尝试创建文件夹
        os.mkdir(dir_name)
        print('文件夹：', dir_name, '创建成功')
    except FileExistsError:
        # 如果文件夹已存在则捕获异常
        print('文件夹：', dir_name, '已经存在')

def getGc(dataItem):
    url = 'https://www.kuwo.cn/openapi/v1/www/lyric/getlyric?httpsStatus=1&reqId=0a232110-17b1-11f0-a7ab-ebfff1a198dd&plat=web_www&from='
    params = {"musicId": dataItem}  # 音乐 ID 作为参数
    resp = requests.get(url, headers=get_kw_headers(), params=params)  # 使用 requests 库发送请求
    if resp.status_code == 200:  # 如果状态码为 200，表示请求成功
        json_data = resp.json()  # 返回的JSON 数据
        # 取出data->lrclist->lineLyric，并且安全访问嵌套字段，若字段不存在则返回空列表或默认值0
        lyrics = [
            item['lineLyric']
            for item in json_data.get('data', {}).get('lrclist', [])
        ] if 'data' in json_data and 'lrclist' in json_data['data'] else 0
        return lyrics  # 返回歌词

if __name__ == "__main__":
    """
    主程序入口，执行批量下载周杰伦所有歌词的操作。
    功能流程：
    1. 遍历所有分页，收集所有歌曲的MUSICRID
    2. 批量获取每首歌曲的歌词
    3. 将歌词保存到本地文件
    """
    search_name = '许嵩'  # 搜索关键词（歌手名）
    all_music_ids = []  # 存储所有找到的音乐ID（MUSICRID）
    page = 1  # 起始页码
    max_retries = 3  # 单页最大重试次数（防止网络波动）
    retry_delay = 2  # 重试等待时间（秒）
    page_delay = 1  # 分页请求间隔（秒，防反爬）
    # ========== 分页数据采集阶段 ==========
    # 遍历所有分页直到无数据
    while True:
        retries = 0  # 当前页重试计数器
        success = False  # 标记当前页是否采集成功
        # 分页重试机制（最多尝试3次）
        while retries < max_retries and not success:
            try:
                print(f"正在抓取第{page}页... ")
                # 调用搜索接口获取当前页数据
                data = search_music(search_name, page=page, rn=30)
                if data:
                    all_music_ids.extend(data)  # 将新发现的musicID加入总列表
                    print(f"第 {page} 页获取成功，新增 {len(data)} 条数据")
                    success = True  # 标记为成功
                else:
                    print(f"第 {page} 页无数据，停止爬取")
                break  # 数据为空时终止循环
            except Exception as e:
                retries += 1
                print(f"请求失败（尝试 {retries}/{max_retries}），错误：{e}")
                time.sleep(retry_delay)  # 等待后重试
        # 连续失败处理
        if not success:
            print("连续请求失败，终止爬取")
            break
        page += 1  # 处理下一页
        time.sleep(page_delay)  # 分页间隔控制

    # ========== 数据处理与保存阶段 ==========
    if all_music_ids:
        mkdir_dir_at_curr_path(search_name)  # 创建以歌手命名的文件夹
        total = len(all_music_ids)  # 总数据量统计
        success_count = 0  # 成功保存计数器
        # ========== 歌词批量获取阶段 ==========
        for idx, music_id in enumerate(all_music_ids, 1):
            try:
                print(f"正在处理第 {idx}/{total} 条数据...")
                lyrics = getGc(music_id)  # 调用歌词获取接口
                if lyrics != 0:  # 确保获取到有效歌词
                    # 生成唯一文件名（musicID前10位 + 歌词首句）
                    filename = f"{lyrics[0][:20]}.txt"
                    # 保存歌词到文件（路径：歌手文件夹/文件名.txt）
                    save_to_json(lyrics, f"{search_name}/{filename}")
                    success_count += 1
                    print(f"进度 {idx}/{total} - 已保存: {filename}")
                else:
                    print(f"歌词获取失败: {music_id}")
            except Exception as e:
                print(f"处理 {music_id} 时发生错误: {e}")
            # ========== 结果统计 ==========
            print(f" 完成！共处理{total}条数据，成功保存{success_count}条歌词")
        else:
            print("未获取到有效数据")
        """
        代码执行流程示意图：
        1. 初始化参数 → 2. 分页采集musicID → 3. 批量获取歌词 → 4. 保存文件
        关键设计：
        - 分页重试机制：应对网络波动和服务器限制
        - 请求间隔控制：避免触发反爬机制
        - 异常处理：保证程序健壮性
        """
